`timescale 1ns / 1ps

`include "data_width.vh"

// 16 => 8
module get_edge_info_pre_2 #(parameter
    VERTEX_PIPE_NUM = `VERTEX_PIPE_NUM,
    TOT_EDGE_MASK_WIDTH = `TOT_EDGE_MASK_WIDTH, TOT_ACC_ID_WIDTH = `TOT_ACC_ID_WIDTH,
    DST_ID_DWIDTH = `DST_ID_DWIDTH, VERTEX_MASK_WIDTH = `VERTEX_MASK_WIDTH
    ) (
        input                                                           clk,
        input                                                           front_rst,
        input                                                           front_finish_read,
        input                                                           front_any_dst_data_valid,
        input [TOT_EDGE_MASK_WIDTH * VERTEX_PIPE_NUM - 1 : 0]           front_tot_src_p_mask,
        input [TOT_ACC_ID_WIDTH * VERTEX_PIPE_NUM - 1 : 0]              front_tot_acc_id,
        input [DST_ID_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]                 front_dst_id,
        input [VERTEX_MASK_WIDTH * VERTEX_PIPE_NUM - 1 : 0]             front_src_p_mask_r,
        input [VERTEX_PIPE_NUM - 1 : 0]                                 front_dst_data_valid,
        input                                                           back_stage_vertex_full,

        output                                                          rst,
        output                                                          buffer_full_vertex,
        output                                                          finish_read,
        output [TOT_EDGE_MASK_WIDTH * (VERTEX_PIPE_NUM / 2) - 1 : 0]    tot_src_p_mask,
        output [TOT_ACC_ID_WIDTH * (VERTEX_PIPE_NUM / 2) - 1 : 0]       tot_acc_id,
        output [DST_ID_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]                dst_id,
        output [VERTEX_MASK_WIDTH * VERTEX_PIPE_NUM - 1 : 0]            src_p_mask_r,
        output [VERTEX_PIPE_NUM - 1 : 0]                                dst_data_valid,
        output                                                          any_dst_data_valid);

    wire                                    finish_buffer_empty, finish_buffer_full;
    wire [(VERTEX_PIPE_NUM / 2) - 1 : 0]    mask_buffer_empty, mask_buffer_full;
    wire [VERTEX_PIPE_NUM - 1 : 0]          dst_buffer_empty, dst_buffer_full;
    wire [VERTEX_PIPE_NUM - 1 : 0]          data_valid;

    assign buffer_full_vertex = dst_buffer_full[0];
    assign any_dst_data_valid = data_valid[0];

    get_edge_info_pre_2_para_trans P (
        .clk        (clk),
        .front_rst  (front_rst),

        .rst        (rst));

    get_edge_info_pre_2_fr FR1 (
        .clk                        (clk),
        .rst                        (front_rst),
        .front_finish_read          (front_finish_read),
        .front_any_dst_data_valid   (front_any_dst_data_valid),
        .back_stage_vertex_full     (back_stage_vertex_full),
        .read_next_valid            (!mask_buffer_empty[0]),

        .buffer_empty               (finish_buffer_empty),
        .buffer_full                (finish_buffer_full),
        .finish_read                (finish_read));

    generate
        genvar i;
        for (i = 0; i < (VERTEX_PIPE_NUM / 2); i = i + 1) begin : M6_2_BLOCK_1
            get_edge_info_pre_2_mask_single M (
                .clk(clk), .rst(front_rst),
                .front_tot_src_p_mask_1(front_tot_src_p_mask[(2 * i + 1) * TOT_EDGE_MASK_WIDTH - 1 : (2 * i) * TOT_EDGE_MASK_WIDTH]),
                .front_tot_src_p_mask_2(front_tot_src_p_mask[(2 * i + 2) * TOT_EDGE_MASK_WIDTH - 1 : (2 * i + 1) * TOT_EDGE_MASK_WIDTH]),
                .front_tot_acc_id_1(front_tot_acc_id[(2 * i + 1) * TOT_ACC_ID_WIDTH - 1 : (2 * i) * TOT_ACC_ID_WIDTH]),
                .front_tot_acc_id_2(front_tot_acc_id[(2 * i + 2) * TOT_ACC_ID_WIDTH - 1 : (2 * i + 1) * TOT_ACC_ID_WIDTH]),
                .front_any_dst_data_valid(front_any_dst_data_valid), .back_stage_vertex_full(back_stage_vertex_full),

                .buffer_empty(mask_buffer_empty[i]), .buffer_full(mask_buffer_full[i]),
                .tot_src_p_mask(tot_src_p_mask[(i + 1) * TOT_EDGE_MASK_WIDTH - 1 : i * TOT_EDGE_MASK_WIDTH]),
                .tot_acc_id(tot_acc_id[(i + 1) * TOT_ACC_ID_WIDTH - 1 : i * TOT_ACC_ID_WIDTH]));
        end
    endgenerate

    generate
        for (i = 0; i < VERTEX_PIPE_NUM; i = i + 1) begin : M6_2_BLOCK_2
            get_edge_info_pre_2_vertex_single V (
                .clk                        (clk),
                .rst                        (front_rst),
                .front_dst_id               (front_dst_id[(i + 1) * DST_ID_DWIDTH - 1 : i * DST_ID_DWIDTH]),
                .front_src_p_mask_r         (front_src_p_mask_r[(i + 1) * VERTEX_MASK_WIDTH - 1 : i * VERTEX_MASK_WIDTH]),
                .front_dst_data_valid       (front_dst_data_valid[i]),
                .front_any_dst_data_valid   (front_any_dst_data_valid),
                .back_stage_vertex_full     (back_stage_vertex_full),
                .read_next_valid            (!mask_buffer_empty[0]),

                .buffer_empty               (dst_buffer_empty[i]),
                .buffer_full                (dst_buffer_full[i]),
                .dst_id                     (dst_id[(i + 1) * DST_ID_DWIDTH - 1 : i * DST_ID_DWIDTH]),
                .src_p_mask_r               (src_p_mask_r[(i + 1) * VERTEX_MASK_WIDTH - 1 : i * VERTEX_MASK_WIDTH]),
                .dst_data_valid             (dst_data_valid[i]),
                .data_valid                 (data_valid[i]));
        end
    endgenerate

endmodule

module get_edge_info_pre_2_para_trans (
    input       clk,
    input       front_rst,

    output reg  rst);

    always @ (posedge clk) begin
        rst <= front_rst;
    end

endmodule

module get_edge_info_pre_2_fr (
    input clk,
    input rst,
    input front_finish_read,
    input front_any_dst_data_valid,
    input back_stage_vertex_full,
    input read_next_valid,

    output buffer_empty,
    output buffer_full,
    output finish_read);

    finish_read_fifo FR1 (
        .clk            (clk),
        .srst           (rst),
        .din            (front_finish_read),
        .wr_en          (front_any_dst_data_valid),
        .rd_en          (!back_stage_vertex_full & read_next_valid),

        .dout           (finish_read),
        .empty          (buffer_empty),
        .prog_full      (buffer_full));

endmodule

module get_edge_info_pre_2_mask_single #(parameter
    EDGE_PIPE_NUM = `EDGE_PIPE_NUM,
    ACC_ID_WIDTH = `ACC_ID_WIDTH, EDGE_MASK_WIDTH = `EDGE_MASK_WIDTH,
    PIPE_BUFFER_SIZE = `PIPE_BUFFER_SIZE, PIPE_BUFFER_PTR_WIDTH = `PIPE_BUFFER_PTR_WIDTH, PIPE_AM_LEVEL = `PIPE_AM_LEVEL
    ) (
        input                                                   clk,
        input                                                   rst,
        input [EDGE_MASK_WIDTH * EDGE_PIPE_NUM - 1 : 0]         front_tot_src_p_mask_1,
        input [EDGE_MASK_WIDTH * EDGE_PIPE_NUM - 1 : 0]         front_tot_src_p_mask_2,
        input [ACC_ID_WIDTH * EDGE_PIPE_NUM - 1 : 0]            front_tot_acc_id_1,
        input [ACC_ID_WIDTH * EDGE_PIPE_NUM - 1 : 0]            front_tot_acc_id_2,
        input                                                   front_any_dst_data_valid,
        input                                                   back_stage_vertex_full,

        output                                                  buffer_empty,
        output                                                  buffer_full,
        output reg [EDGE_MASK_WIDTH * EDGE_PIPE_NUM - 1 : 0]    tot_src_p_mask,
        output reg [ACC_ID_WIDTH * EDGE_PIPE_NUM - 1 : 0]       tot_acc_id);

    wire [ACC_ID_WIDTH * EDGE_PIPE_NUM - 1 : 0]     top_tot_acc_id_1;
    wire [ACC_ID_WIDTH * EDGE_PIPE_NUM - 1 : 0]     top_tot_acc_id_2;
    wire [EDGE_MASK_WIDTH * EDGE_PIPE_NUM - 1 : 0]  top_tot_src_p_mask_1;
    wire [EDGE_MASK_WIDTH * EDGE_PIPE_NUM - 1 : 0]  top_tot_src_p_mask_2;

    tot_acc_id_fifo_fall_through af1 (
        .clk(clk), .srst(rst),
        .din(front_tot_acc_id_1), .wr_en(front_any_dst_data_valid), .rd_en(!back_stage_vertex_full),

        .dout(top_tot_acc_id_1),
        .empty(buffer_empty), .prog_full(buffer_full));

    tot_acc_id_fifo_fall_through af2 (
        .clk(clk), .srst(rst),
        .din(front_tot_acc_id_2), .wr_en(front_any_dst_data_valid), .rd_en(!back_stage_vertex_full),

        .dout(top_tot_acc_id_2));

    tot_src_p_mask_fifo_fall_through sf1 (
        .clk(clk), .srst(rst),
        .din(front_tot_src_p_mask_1), .wr_en(front_any_dst_data_valid), .rd_en(!back_stage_vertex_full),

        .dout(top_tot_src_p_mask_1));

    tot_src_p_mask_fifo_fall_through sf2 (
        .clk(clk), .srst(rst),
        .din(front_tot_src_p_mask_2), .wr_en(front_any_dst_data_valid), .rd_en(!back_stage_vertex_full),

        .dout(top_tot_src_p_mask_2));

    // output
    always @ (posedge clk) begin
        if (rst) begin
            tot_src_p_mask <= 0;
        end
        else begin
            if (!(buffer_empty || back_stage_vertex_full)) begin
                tot_src_p_mask <= (top_tot_src_p_mask_1 | top_tot_src_p_mask_2);
            end
            else begin
                tot_src_p_mask <= 0;
            end
        end
    end
    generate
        genvar i;
        for (i = 0; i < EDGE_PIPE_NUM; i = i + 1) begin : M6_2_BLOCK_3
            always @ (posedge clk) begin
               if (rst) begin
                    tot_acc_id[(i + 1) * ACC_ID_WIDTH - 1 : i * ACC_ID_WIDTH] <= {ACC_ID_WIDTH{1'b1}};
                end
                else begin
                    if (!(buffer_empty || back_stage_vertex_full)) begin
                        if (top_tot_src_p_mask_1[i] && !top_tot_src_p_mask_2[i]) begin
                            tot_acc_id[(i + 1) * ACC_ID_WIDTH - 1 : i * ACC_ID_WIDTH] <= top_tot_acc_id_1[(i + 1) * ACC_ID_WIDTH - 1 : i * ACC_ID_WIDTH];
                        end
                        else begin
                            tot_acc_id[(i + 1) * ACC_ID_WIDTH - 1 : i * ACC_ID_WIDTH] <= top_tot_acc_id_2[(i + 1) * ACC_ID_WIDTH - 1 : i * ACC_ID_WIDTH];
                        end
                    end
                    else begin
                        tot_acc_id[(i + 1) * ACC_ID_WIDTH - 1 : i * ACC_ID_WIDTH] <= {ACC_ID_WIDTH{1'b1}};
                    end
                end
            end
        end
    endgenerate

endmodule

module get_edge_info_pre_2_vertex_single #(parameter
    DST_ID_DWIDTH = `DST_ID_DWIDTH,
    VERTEX_MASK_WIDTH = `VERTEX_MASK_WIDTH
    ) (
        input                               clk,
        input                               rst,
        input [DST_ID_DWIDTH - 1 : 0]       front_dst_id,
        input [VERTEX_MASK_WIDTH - 1 : 0]   front_src_p_mask_r,
        input                               front_dst_data_valid,
        input                               front_any_dst_data_valid,
        input                               back_stage_vertex_full,
        input                               read_next_valid,

        output                              buffer_empty,
        output                              buffer_full,
        output                              data_valid,
        output [DST_ID_DWIDTH - 1 : 0]      dst_id,
        output [VERTEX_MASK_WIDTH - 1 : 0]  src_p_mask_r,
        output                              dst_data_valid);

    dst_id_fifo DI1 (
        .clk(clk), .srst(rst),
        .din(front_dst_id), .wr_en(front_any_dst_data_valid), .rd_en(!back_stage_vertex_full & read_next_valid),

        .dout(dst_id), .empty(buffer_empty), .prog_full(buffer_full));

    vertex_mask_fifo VM1 (
        .clk(clk), .srst(rst),
        .din(front_src_p_mask_r), .wr_en(front_any_dst_data_valid), .rd_en(!back_stage_vertex_full & read_next_valid),

        .dout(src_p_mask_r));

    valid_fifo DDVF1 (
        .clk(clk), .srst(rst),
        .din(front_dst_data_valid), .wr_en(front_any_dst_data_valid), .rd_en(!back_stage_vertex_full & read_next_valid),
        
        .dout(dst_data_valid), .valid(data_valid));

endmodule